package org.scala.javafx.diagnostics

import javafx.stage.Stage
import javafx.stage.StageStyle
import javafx.scene.Scene
import javafx.scene.layout.Pane
import javafx.scene.Group
import javafx.stage.PopupWindow
import javafx.scene.shape.Path
import javafx.scene.shape.MoveTo
import javafx.scene.shape.LineTo
import javafx.scene.shape.ClosePath
import javafx.stage.Popup
import javafx.stage.Window
import scala.collection.mutable.WeakHashMap
import org.scala.javafx.rich.Enrich._
import com.sun.glass.ui.Screen
import javafx.scene.input.MouseEvent
import javafx.event.EventType
import javafx.scene.Node

class SpyScopeGlass(owner: Window) extends Stage(StageStyle.TRANSPARENT) {
  val screenGroup = new Group()
  val windowGroup = new Group()
  val sceneGroup = new Group()
  val scene = new Scene(screenGroup) {
    override def impl_processMouseEvent(mouseEvent: MouseEvent): Unit = {
      val newMouseEvent = MouseEvent.impl_mouseEvent(
          mouseEvent.getX() - owner.getX() - owner.getScene().getX(),
          mouseEvent.getY() - owner.getY() - owner.getScene().getY(),
          mouseEvent.getScreenX(),
          mouseEvent.getScreenY(),
          mouseEvent.impl_getWheelRotation(),
          mouseEvent.getButton(),
          mouseEvent.getClickCount(),
          mouseEvent.isShiftDown(),
          mouseEvent.isControlDown(),
          mouseEvent.isAltDown(),
          mouseEvent.isMetaDown(),
          MouseEvent.impl_getPopupTrigger(mouseEvent),
          mouseEvent.isPrimaryButtonDown(),
          mouseEvent.isMiddleButtonDown(),
          mouseEvent.isSecondaryButtonDown(),
          mouseEvent.getEventType().asInstanceOf[EventType[MouseEvent]])

      if (SpyScopeGlass.this.isFocused()) {
        owner.setFocused(true)
        owner match {
          case ownerStage: Stage => ownerStage.toFront()
          case _ =>
        }
      }
      
      owner.getScene().impl_processMouseEvent(newMouseEvent)
    }
  }
  screenGroup.getChildren().add(windowGroup)
  windowGroup.getChildren().add(sceneGroup)
  scene.setFill(null)
  sceneGroup.layoutXProperty().bind(owner.getScene().xProperty())
  sceneGroup.layoutYProperty().bind(owner.getScene().yProperty())
  this.setScene(scene)
  this.setX(0.0)
  this.setY(0.0)
  owner.xProperty().addChangeListener { (_, _, newValue) =>
    windowGroup.setLayoutX(newValue.doubleValue())
  }
  owner.yProperty().addChangeListener { (_, _, newValue) =>
    windowGroup.setLayoutY(newValue.doubleValue())
  }
  
  this.windowGroup.setDisable(true)
  this.initOwner(owner)
  this.show()
}

object SpyScopeGlass {
  private val spyScopeGlasses = new WeakHashMap[Window, SpyScopeGlass]
  private val lock = new Object
  
  def on(window: Window): SpyScopeGlass = {
    this.lock.synchronized {
      spyScopeGlasses.getOrElseUpdate(window, new SpyScopeGlass(window))
    }
  }

  def onAny(any: Any): Option[SpyScopeGlass] = any match {
    case window: Window => this.lock.synchronized { Some(SpyScopeGlass.on(window)) }
    case scene: Scene => scene.getWindow() match {
      case null => None
      case window => this.lock.synchronized { Some(SpyScopeGlass.on(window))}
    }
    case node: Node => node.getScene() match {
      case null => None
      case scene => scene.getWindow() match {
        case null => None
        case window => this.lock.synchronized { Some(SpyScopeGlass.on(window))}
      }
    }
    case _ => None
  }
}
